#pragma once
#include <iostream>
#include <unistd.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <jsoncpp/json/json.h>
#include <atomic>
#include <fstream>
#include <sstream>

#include <boost/algorithm/string.hpp>


namespace ns_util
{
    // 所有临时文件都在该路径下
    const static std::string tmp_path = "../compile_server/temp/";

    // 路径操作类
    class PathUtil
    {
    public:
        PathUtil(){}
        ~PathUtil(){}


        // 为文件添加指定后缀
        static std::string AddSuffix(std::string file_name, std::string suffix)
        {
            std::string res = tmp_path;
            res += file_name;
            res += suffix;

            return res;
        }

        // 为文件添加 路径 + cpp后缀 （源文件）
        static std::string Src(std::string file_name)
        {
            return AddSuffix(file_name, ".cpp");
        }

        // 为文件添加 路径 + exe后缀 （可执行文件）
        static std::string Exe(std::string file_name)
        {
            return AddSuffix(file_name, ".exe");
        }

        // 为文件添加 路径 + stderr后缀 （编译错误信息文件）
        static std::string CompileError(std::string file_name)
        {
            return AddSuffix(file_name, ".compile_err");
        }


        //运行模块额外需求

        //数据输入文件
        static std::string Stdin(std::string proc_name)
        {
            return AddSuffix(proc_name, ".stdin");
        } 

        //运行结果输出文件
        static std::string Stdout(std::string proc_name)
        {
            return AddSuffix(proc_name, ".stdout");
        }

        static std::string Stderr(std::string proc_name)
        {
            return AddSuffix(proc_name, ".stderr");
        }
        


    };


    // 时间类
    class TimeUtil
    {
    public:
        //获得时间戳
        static std::string GetTimeStamp()
        {
            struct timeval tv;
            if(gettimeofday(&tv, nullptr) == -1)
            {
                std::cout << "gettimeofday error" << std::endl;
                exit(-1);
            }

            return std::to_string(tv.tv_sec);
        }

        //获得毫秒级时间戳
        static std::string GetTimeMS()
        {
            struct timeval tv;
            if(gettimeofday(&tv, nullptr) == -1)
            {
                std::cout << "gettimeofday error" << std::endl;
                exit(-1);
            }

            /**
             * 1秒 = 1000毫秒
             * 1毫秒 = 1000微秒
            */
            std::string res = std::to_string(tv.tv_sec * 1000 + tv.tv_usec / 1000);
            
            return res;
        }
    };


    // 文件操作类
    class FileUtil
    {
    public:

        static bool IsExist(std::string path_name)
        {
            /**
             * stat函数：获取文件属性
             * 获取成功：return 0
             * 获取失败：return -1
             */

            struct stat st;
            if (stat(path_name.c_str(), &st) == 0)
                return true;

            return false;
        }

        /**
         * 形成唯一文件名
         * 1.获得毫秒级时间戳
         * 2.加上原子性递增数
        */
        static std::string UniqueFile()
        {
            std::string res = TimeUtil::GetTimeMS();
            
            static std::atomic<int> uuid(0);
            uuid++;
            
            return res + "_" + to_string(uuid);
        }

        /**
         * 写文件
         * 写到哪，写什么
        */
        static bool WriteFile(const std::string& file_name, const std::string &code)
        {
            std::ofstream ofs(file_name);
            if( !ofs.is_open())
            {
                return false;
            }

            ofs.write(code.c_str(), code.size());
            ofs.close();
            return true;
        }

        /**
         * 读文件
         * 从哪读，读到哪
         * 
         * getline 默认以 '\n' ro 空格分隔，并且不保留分隔符，这可能会导致bug，有时可能需要保留，所以要处理
         * 
         * keep：上层是否需要保留\n，默认为false
         * 保留：不解释\n
         * 不保留：解释\n（换行）
         * 
         * */
        static bool ReadFile(const std::string& src_file, std::string& target, bool keep = false)
        {
            target.clear();

            std::ifstream ifs(src_file);
            if( !ifs.is_open())
            {
                return false;
            }

            //持续读
            std::string line;
            while(getline(ifs, line))
            {
                target += line;
                target += (keep ? "\n" : "");
            }

            ifs.close();

            return true;
        } 

        /**
         * 删除文件：
        */
        static void RemoveTempFile(std::string file_name)
        {
            std::string _src = PathUtil::Src(file_name);
            std::string _exe = PathUtil::Exe(file_name);
            std::string _compile_err = PathUtil::CompileError(file_name);
            std::string _stdin = PathUtil::Stdin(file_name);
            std::string _stdout = PathUtil::Stdout(file_name);
            std::string _stderr = PathUtil::Stderr(file_name);

            if(IsExist(_src))   unlink(_src.c_str());
            if(IsExist(_exe))   unlink(_exe.c_str());
            if(IsExist(_compile_err))   unlink(_compile_err.c_str());
            if(IsExist(_stdin))   unlink(_stdin.c_str());
            if(IsExist(_stdout))   unlink(_stdout.c_str());
            if(IsExist(_stderr))   unlink(_stderr.c_str());
        }
    };


    class StringUtil
    {
        /**
         * 切割字符串
         * 参数1：字符串
         * 参数2：保存子串的vector
         * 参数3：分隔符
         * 参数4：是否压缩
         * 比如： hello:world ,以:为分隔符，则结果 hello，world
         * hello::::::world ，如果不压缩，结果就是 hello,"","","","","" world，中间有多个空串
         * （注意，空串，没有符号，不是空格）
         * 如果压缩，那就是 hello, world
        */
       public:
        // static void Split(std::string& str, std::vector<std::string>& substr, char sep, bool compress = true)
        // {
        //     std::stringstream strstr(str);
        //     std::string sub;
        //     while(std::getline(strstr, sub, sep))
        //     {
        //         if(sub == "" && compress)
        //         {
        //             continue;
        //         }

        //         substr.push_back(sub);
        //     }
        // }


        static void Split(const std::string& str, std::vector<std::string>& substr, const std::string& sep)
        {
            /***
             * boost::split
             * 参数1：容器
             * 参数2：源字符串
             * 参数3：分隔符
             * 参数4：是否压缩
             */
            boost::split(substr, str, boost::is_any_of(sep), boost::algorithm::token_compress_on);
        }
    };

    
}
